/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd.context;

import java.util.Map.Entry;

import org.zhiqim.httpd.HttpResource;
import org.zhiqim.httpd.context.config.ZAction;
import org.zhiqim.httpd.context.config.ZActionPackage;
import org.zhiqim.httpd.context.config.ZAttribute;
import org.zhiqim.httpd.context.config.ZComponent;
import org.zhiqim.httpd.context.config.ZComponentWapper;
import org.zhiqim.httpd.context.config.ZConfig;
import org.zhiqim.httpd.context.config.ZInterceptor;
import org.zhiqim.httpd.context.core.Action;
import org.zhiqim.httpd.context.core.Interceptor;
import org.zhiqim.httpd.context.extend.EmptyAction;
import org.zhiqim.httpd.websocket.HttpWebSocketService;
import org.zhiqim.kernel.Z;
import org.zhiqim.kernel.annotation.AnNullable;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.model.maps.HashMapSO;
import org.zhiqim.kernel.model.maps.HashMapSV;
import org.zhiqim.kernel.model.maps.LinkedMapSV;
import org.zhiqim.kernel.model.maps.MapS;
import org.zhiqim.kernel.util.Arrays;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Classes;
import org.zhiqim.kernel.util.Ints;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.zml.Expression;
import org.zhiqim.zml.Zml;
import org.zhiqim.zml.ZmlEngine;
import org.zhiqim.zml.ZmlVarNotice;
import org.zhiqim.zml.ZmlVariable;
import org.zhiqim.zml.exception.ExpressionException;
import org.zhiqim.zml.statement._Var;


/**
 * 上下文环境配置，针对FContext的多种情况进行处理
 * 1.先加载组件的context
 * 2.再加载子context
 * 3.最后加载本身context
 * 后加载的可以调用和覆盖先加载的属性、变量、拦截器和动作
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class ZmlConfig implements ZmlContextConstants, ZmlVarNotice, MapS
{
    private static final Log log = LogFactory.getLog(ZmlConfig.class);
    private static final EmptyAction empty = new EmptyAction();
    
    private final ZmlContext context;
    private final boolean pathExactPrior;
    private final ZConfig rootConfig;
    
    private ZmlBootstrap bootstrap;
    private ZActionPackage actionPackage;
    
    private LinkedMapSV<ZComponentWapper> componentMap = new LinkedMapSV<>();
    private HashMapSV<HashMapSO> variableMap = new HashMapSV<>();
    private HashMapSO attributeMap = new HashMapSO();
    private HashMapSV<HttpWebSocketService> websocketMap = new HashMapSV<>();
    private HashMapSV<Interceptor> interceptorMap = new HashMapSV<>();
    private HashMapSV<Action> actionMap = new HashMapSV<>();
    
    /**
     * 构造函数，提供配置根路径和是否是打包在类路径
     * 
     * @param context           上下文环境
     * @param resource          上下文件资源
     * @param pathExactPrior    路径寻找精确优先
     */
    public ZmlConfig(ZmlContext context, HttpResource resource, boolean pathExactPrior)
    {
        this.context = context;
        this.pathExactPrior = pathExactPrior;
        if (resource.isValid())
        {//有配置资源的
            this.rootConfig = new ZConfig(this, true, resource.getResourceType(), resource.getResourcePath(), ZC_CONFIG, ZC_CONF_CONFIG_XML);
        }
        else
        {//没有配置资源的
            this.rootConfig = null;
            this.actionPackage = null;
            this.bootstrap = new ZmlBootstrap();
            this.bootstrap.setContext(context);
        }
    }
    
    public ZmlContext getContext()
    {
        return context;
    }
    
    public ZConfig getRootConfig()
    {
        return rootConfig;
    }
    
    public String getActionPackage()
    {
        return actionPackage==null?null:actionPackage.getActionPackage();
    }
    
    public ZComponentWapper getComponent(String path)
    {
        return componentMap.get(path);
    }
    
    /** 创建 */
    public boolean create() throws Exception
    {//分三步，第一步:加载根配置，第二步:初始化根配置和组件配置，第三步:组装上下文ZML和执行引导
        
        if (rootConfig == null)
        {//如果没有根配置则初始化完成
            return true;
        }
        
        /***************************************************************************/
        //第一步，加载根配置和组件配置
        /***************************************************************************/
        
        //1.1 加载根配置（配置文件和Action包）
        rootConfig.load();
        actionPackage = rootConfig.getActionPackage(this);
        if (actionPackage != null)
            actionPackage.load();
        
        //1.2.先封装成组件包，再加载组件配置
        for (ZComponent c : rootConfig.getComponentList())
        {
            ZComponentWapper component = new ZComponentWapper(this, c);
            componentMap.put(c.getPath(), component);
            
            component.load();
        }
        
        /***************************************************************************/
        //第二步:初始化组件配置
        /***************************************************************************/
        
        int resourceIndex = 0;ZmlEngine engine = context.getZmlEngine();
        for (ZComponentWapper component : componentMap.values())
        {
            ZmlBootstrap bootstrap = component.getBootstrap();
            if (bootstrap != null && !bootstrap.isBefore())
            {
                bootstrap.initBefore();
                bootstrap.setBefored();
            }
            
            //2.1 有资源目录
            //2.1.1 有上下文配置
            ZConfig rc = component.getConfig();
            if (rc != null)
            {//有context.xml
                for (ZConfig c : rc.getConfigList())
                {
                    if (!initConfig(c))
                        return false;
                }
                
                if (!initConfig(rc))
                    return false;
            }

            //2.1.2 组件资源目录，用于静态文件覆盖和读取
            ((StaticContext)context).addComponentResource(resourceIndex++, _CLASSPATH_, component.getPath());
            
            //2.1.3 组件配置加载
            if (engine != null)
            {
                engine.addComponentZmlLoader(ZmlContext.class, component.getPath());
                engine.addConfigZml(ZC_CONF_CONFIG_ZML, component.getPath());
            }
            
            //2.2 有引导类
            if (bootstrap != null && !bootstrap.isAfter())
            {
                bootstrap.initAfter();
                bootstrap.setAftered();
            }
        }
        
        /***************************************************************************/
        //第三步:初始化根配置
        /***************************************************************************/
        
        //3.1.最后初始化根配置的下配置的context
        for (ZConfig c : rootConfig.getConfigList())
        {
            if (!initConfig(c))
                return false;
        }
        
        //3.2.最后初始化根配置config.xml
        if (!initConfig(rootConfig))
            return false;
        
        //3.3.最后初始化前部引导
        ZmlBootstrap bootstrap = getBootstrap();
        if (!bootstrap.isBefore())
        {
            bootstrap.initBefore();
            bootstrap.setBefored();
        }
        
        //3.4.最后组装根配置的上下文环境ZML（注，根引导类不在这里启动）
        if (engine != null)
        {
            engine.addConfigZml(ZC_CONF_CONFIG_ZML);
        }
        
        //3.5.最后初始化后部引导
        if (!bootstrap.isAfter())
        {
            bootstrap.initAfter();
            bootstrap.setAftered();
        }
        
        return true;
    }
    
    /** 销毁 */
    public void destroy()
    {
        if (bootstrap != null)
            bootstrap.close();
        
        for (ZComponentWapper component : componentMap.values())
        {
            ZmlBootstrap bootstrap = component.getBootstrap();
            if (bootstrap != null){
                bootstrap.close();
            }
        }
        
        componentMap.clear();
    }
    
    /************************************************************************************************/
    //获取 Variable & Attribute
    /************************************************************************************************/
    
    public boolean has(String key)
    {
        if (context.hasTopAttribute(key))
        {//1.查顶级
            return true;
        }
        
        //2.顶级没有，优先变量表
        if (context.getZmlEngine() != null)
            context.getZmlEngine().chkCurConfigModified();
        
        for (HashMapSO vMap : variableMap.values())
        {
            if (vMap.containsKey(key))
                return true;
        }
        
        //3.最后取属性表
        return attributeMap.containsKey(key);
    }
    
    /** 变量表优先，即config.zml优先于config.xml */
    public Object get(String key)
    {
        if (context.hasTopAttribute(key))
        {//1.如果顶级属性里有，则取属性，值为null也返回
            return context.getTopAttribute(key);
        }
        
        //2.顶级没有，优先变量表
        if (context.getZmlEngine() != null)
            context.getZmlEngine().chkCurConfigModified();
        
        for (HashMapSO vMap : variableMap.values())
        {
            Object value = vMap.get(key);
            if (value != null)//动态检查
                return value;
        }
        
        //3.最后取属性表
        return attributeMap.get(key);
    }
    
    @AnNullable /** 根据协议获取WS服务 */
    public synchronized HttpWebSocketService getWebSocketService(String protocol)
    {
        if (protocol == null)
        {//1.未传协议名，以缺省协议名查找服务
            protocol = _DEFAULT_WEB_SOCKET_PROTOCOL_;
        }
        
        if (websocketMap.containsKey(protocol))
        {//2.非第一次查找服务，以上次的为准
            return websocketMap.get(protocol);
        }
        
        //支持协议名为类名或类别名（通过@AnAlias注解）
        Object obj = Z.cls().newInstance(protocol);
        if (obj == null || !(obj instanceof HttpWebSocketService))
        {//3.1 未定义协议名的服务，或定义的服务不正确，把null放置到表中
            websocketMap.put(protocol, null);
            return null;
        }
        else
        {//3.2 有定义协议服务的，保存以等待后续直接返回
            HttpWebSocketService websocket = (HttpWebSocketService)obj;
            websocket.setWebSocketManager(context.getWebSocketManager());
            websocketMap.put(protocol, websocket);
            return websocket;
        }
    }
    
    /** 获取引导类 */
    public ZmlBootstrap getBootstrap()
    {
        if (bootstrap != null)
            return bootstrap;
        
        String bootstrapClass = rootConfig.getBootstrapClass();
        if (Validates.isNotEmptyBlank(bootstrapClass))
        {//根配置有，以根配置为准
            Object instance = Classes.newInstance(bootstrapClass);
            Asserts.as((instance instanceof ZmlBootstrap)?null:"组件["+context.getContextName()+"]配置的[bootstrap]或不是ZmlBootstrap或其子类");
            bootstrap = (ZmlBootstrap)instance;
            bootstrap.setContext(context);
            return bootstrap;
        }
        
        //从组件中找到最后一个
        for (int i=componentMap.size()-1;i>=0;i--)
        {
            ZComponentWapper component = componentMap.index(i);
            bootstrap = component.getBootstrap();
            if (bootstrap != null)
                return bootstrap;
        }
        
        //都没有，取默认
        bootstrap = new ZmlBootstrap();
        bootstrap.setContext(context);
        return bootstrap;
    }

    @Override/** 组件的配置ZML只在第一次加载时放置到属性表，当前配置ZML可能修改放置变量表 */
    public void doUpdateVariable(Zml zml, String configPath, String componentPath, LinkedMapSV<_Var> varMap)
    {
        HashMapSO vMap = null;
        if (Validates.isNotEmpty(componentPath))
        {//组件取属性表
            vMap = attributeMap;
        }
        else
        {//当前根配置ZML
            vMap = variableMap.get(configPath);
            if (vMap == null)
            {//新建
                vMap = new HashMapSO();
                variableMap.put(configPath, vMap);
            }
            else
            {//清理重建
                vMap.clear();
            }
        }
        
        HashMapSO localMap = new HashMapSO();
        localMap.put("context", context);
        
        ZmlEngine engine = zml.getEngine();
        ZmlVariable variable = new ZmlVariable();
        variable.setZml(zml);
        variable.setVariableMap(vMap);
        variable.setContextMap(this);
        variable.addLocalVariable(localMap);
        
        for (Entry<String, _Var> entry : varMap.entrySet())
        {
            String name = entry.getKey();
            Expression expression = entry.getValue().getExpression();
            
            try
            {
                Object value = expression.build(variable);
                vMap.put(name, value);
                
                if (engine != null)
                {
                    //顶级属性没有配置ZmlContext参数时，如下两个参数可由变量设置
                    if (_ZML_MAX_KEEP_TIME_.equals(name) && !context.hasTopAttribute(name))
                    {
                        int val = Ints.toInt(value, 24*60*60);//24小时
                        engine.setMaxKeepTime(val);
                    }
                    else if (_ZML_MAX_IDLE_TIME_.equals(name) && !context.hasTopAttribute(name))
                    {
                        int val = Ints.toInt(value, 1*60*60);//1小时
                        engine.setMaxIdleTime(val);
                    }
                }
            }
            catch (ExpressionException e)
            {
                //异常表示解析失败，不加入到变量表
            }
        }
    }
    
    /************************************************************************************************/
    //获取 ZAction
    /************************************************************************************************/
    
    public ZAction getAction(String path)
    {
        if (path == null)
            return null;
        
        //精确或循序优先
        return pathExactPrior?getExactPriorAction(path):getInTurnAction(path);
    }
    
    private ZAction getInTurnAction(String path)
    {//循序方式
        //1.先查根配置
        if (rootConfig != null)
        {
            ZAction action = rootConfig.getInTurnAction(path);
            if (action != null)
                return action;
        }
        
        //2.再查actionPackage
        if (actionPackage != null)
        {
            ZAction action = actionPackage.getInTurnAction(path);
            if (action != null)
                return action;
        }
        
        //3.再倒序查组件
        for (int i=componentMap.size()-1;i>=0;i--)
        {
            ZComponentWapper component = componentMap.index(i);
            ZAction action = component.getActionInTurn(path);
            if (action != null)
                return action;
        }
        
        return null;
    }
    
    private ZAction getExactPriorAction(String path)
    {
        //1.精确优先
        
        //1.1.先查根配置
        if (rootConfig != null)
        {
            ZAction action = rootConfig.getExactAction(path);
            if (action != null)
                return action;
        }
        
        //1.2.再查actionPackage
        if (actionPackage != null)
        {
            ZAction action = actionPackage.getExactAction(path);
            if (action != null)
                return action;
        }
        
        //1.3.再倒序查组件
        for (int i=componentMap.size()-1;i>=0;i--)
        {
            ZComponentWapper component = componentMap.index(i);
            ZAction action = component.getExactAction(path);
            if (action != null)
                return action;
        }
        
        //2.模糊再次
        
        //2.1.先查根配置
        if (rootConfig != null)
        {
            ZAction action = rootConfig.getFuzzyAction(path);
            if (action != null)
                return action;
        }
        
        //2.2.再查actionPackage
        if (actionPackage != null)
        {
            ZAction action = actionPackage.getFuzzyAction(path);
            if (action != null)
                return action;
        }
        
        //2.3.再倒序查组件
        for (int i=componentMap.size()-1;i>=0;i--)
        {
            ZComponentWapper component = componentMap.index(i);
            ZAction action = component.getFuzzyAction(path);
            if (action != null)
                return action;
        }
        
        return null;
    }
    
    /************************************************************************************************/
    //设置和获取 Action
    /************************************************************************************************/
    
    public void setActionOnPath(String path, Action action)
    {
        actionMap.put(path, action);
    }
    
    public Action getAction(ZAction zAction)
    {
        Action action = actionMap.get(zAction.getPath());
        if (action != null)
        {//精确匹配path-->>> Action
            return action;
        }
        
        //模糊匹配的通过类名查找
        String className = zAction.getClassName();
        action = getActionByRuntimeClass(className);
        if (action != null)
        {//已被匹配过一次
            return action;
        }
        
        Object obj = Classes.newInstance(className);
        Asserts.as((obj != null && obj instanceof Action)?null:"Action[%s]替换通配符后的类[%s]不存在或不是Action类", zAction.getPath(), className);
        
        action = (Action)obj;
        setActionOnRuntimeClass(className, action);
        return action;
    }
    
    public Action getActionInstance(String clazz)
    {
        if (clazz == null)
            return null;
        
        clazz = Z.cls().className(clazz);
        
        //1.先判断通配的可能性_match_class方式
        Action action = getActionByRuntimeClass(clazz);
        if (action != null)
            return action;
        
        //2.再通过类找到路径，从路径中匹配
        return pathExactPrior?getExactPriorActionByClass(clazz):getInTurnActionByClass(clazz);
    }
    
    private Action getInTurnActionByClass(String clazz)
    {
        //1.先查根配置
        if (rootConfig != null)
        {
            ZAction action = rootConfig.getInTurnActionByClass(clazz);
            if (action != null)
                return getAction(action);
        }
        
        //2.再查actionPackage
        if (actionPackage != null)
        {
            ZAction action = actionPackage.getInTurnActionByClass(clazz);
            if (action != null)
                return getAction(action);
        }
        
        //3.再倒序查组件
        for (int i=componentMap.size()-1;i>=0;i--)
        {
            ZComponentWapper component = componentMap.index(i);
            ZAction action = component.getInTurnActionByClass(clazz);
            if (action != null)
                return getAction(action);
        }
        
        return null;
    }
    
    private Action getExactPriorActionByClass(String clazz)
    {
        //1.精确优先
        
        //1.1.先查根配置
        if (rootConfig != null)
        {
            ZAction action = rootConfig.getExactActionByClass(clazz);
            if (action != null)
                return getAction(action);
        }
        
        //1.2.再查actionPackage
        if (actionPackage != null)
        {
            ZAction action = actionPackage.getExactActionByClass(clazz);
            if (action != null)
                return getAction(action);
        }
        
        //1.3.再倒序查组件
        for (int i=componentMap.size()-1;i>=0;i--)
        {
            ZComponentWapper component = componentMap.index(i);
            ZAction action = component.getExactActionByClass(clazz);
            if (action != null)
                return getAction(action);
        }
        
        //2.模糊再次
        
        //2.1.先查根配置
        if (rootConfig != null)
        {
            ZAction action = rootConfig.getFuzzyActionByClass(clazz);
            if (action != null)
                return getAction(action);
        }
        
        //2.2.再查actionPackage
        if (actionPackage != null)
        {
            ZAction action = actionPackage.getFuzzyActionByClass(clazz);
            if (action != null)
                return getAction(action);
        }
        
        //2.3.再倒序查组件
        for (int i=componentMap.size()-1;i>=0;i--)
        {
            ZComponentWapper component = componentMap.index(i);
            ZAction action = component.getFuzzyActionByClass(clazz);
            if (action != null)
                return getAction(action);
        }
        
        return null;
    }
    
    /************************************************************************************************/
    // interceptor
    /************************************************************************************************/
    
    public boolean hasInterceptor(String interceptor)
    {
        if (interceptorMap.containsKey(interceptor))
            return true;
        
        Class<?> clazz = Z.cls().get(interceptor);
        return (clazz != null && Classes.isImplement(clazz, Interceptor.class));
    }
    
    public void setInterceptor(String alias, Interceptor interceptor)
    {
        interceptorMap.put(alias, interceptor);
    }
    
    public Interceptor getInterceptor(String interceptor)
    {
        Interceptor ic = interceptorMap.get(interceptor);
        if (ic != null)
            return ic;
        
        Class<?> cls = Z.cls().get(interceptor);
        if (cls == null || !Classes.isImplement(cls, Interceptor.class))
            return null;
        
        return (Interceptor)Z.glb().getc(cls);
    }
    
    /************************************************************************************************/
    // private load & check
    /************************************************************************************************/
    
    /**
     * 加载上下文配置信息
     * 
     * @param config       上下文配置信息
     * @return              =true表示加载成功，=false表示加载失败
     * @throws Exception    异常
     */
    private boolean initConfig(ZConfig config) throws Exception
    {
        //1.设置属性配置
        for (ZAttribute attribute : config.getAttributeList())
        {
            attributeMap.put(attribute.getKey(), attribute.getValue());
            log.info(attribute);
        }
        
        //2.设置拦截器
        for (ZInterceptor interceptor : config.getInterceptorList())
        {
            String clazz = interceptor.getClazz();
            Object obj = Classes.newInstance(clazz);
            if (obj == null)
            {
                log.error("interceptor配置[%s]未找到，请检查类名或类包名是否正确", clazz);
                return false;
            }
            
            if (!(obj instanceof Interceptor))
            {
                log.error("interceptor配置未实现Interceptor接口[%s]", clazz);
                return false;
            }
            
            setInterceptor(interceptor.getKey(), (Interceptor)obj);
            log.info(interceptor);
        }
        
        //3.检查Action有效性
        
        //3.1 检查Action变量名和模拟排序
        config.chkActionVariableAndSort();
        
        //3.2 检查精确Action的拦截器、类名和会话名
        for (ZAction action : config.getExactActionList())
        {
            if (!chkInterceptor(action))
                return false;
            
            if (!chkClass(action))
                return false;
            
            log.info(action);
        }
        
        //3.3 检查可匹配Action拦截器、类名和会话名
        for (ZAction action : config.getFuzzyActionList())
        {
            if (!chkInterceptor(action))
                return false;
            
            if (!chkClass(action))
                return false;
            
            log.info(action);
        }
        
        return true;
    }

    /** 检查拦截器 */
    private boolean chkInterceptor(ZAction action)
    {
        String interceptors = action.getInterceptor();
        if (Validates.isEmptyBlank(interceptors))
            return true;
        
        String path = action.getPath();
        String[] interceptorArr = Arrays.toStringArray(interceptors);
        for (String interceptor : interceptorArr)
        {
            if (!hasInterceptor(interceptor))
            {
                log.error("action配置未找到拦截器[%s]][%s]", interceptor, path);
                return false;
            }
        }
        
        return true;
    }
    
    /** 检查拦截器 */
    private boolean chkClass(ZAction action) throws Exception
    {
        String path = action.getPath();
        String clazz = action.getClazz();
        if (Validates.isEmptyBlank(clazz))
        {//未配置class或class为空白的
            actionMap.put(path, empty);
        }
        else if (path.indexOf("*") != -1 && clazz.indexOf("{") != -1 && clazz.indexOf("}") != -1)
        {//class有通配要求的不检查action，在运行时再检查
        }
        else
        {//其他的要求检查
            Object obj = Classes.newInstance(clazz);;
            if (obj == null)
            {
                log.error("action配置[%s]未找到，请检查类名或类包名是否正确", clazz);
                return false;
            }
            
            if (!(obj instanceof Action))
            {
                log.error("action配置[%s]未实现Action接口", clazz);
                return false;
            }
            
            actionMap.put(path, (Action)obj);
        }
        
        return true;
    }
    
    private Action getActionByRuntimeClass(String className)
    {
        return actionMap.get(ZC_MATCH_PREFIX + className);
    }
    
    private void setActionOnRuntimeClass(String className, Action action)
    {
        actionMap.put(ZC_MATCH_PREFIX + className, action);
    }
}
